2

年终总结结果到这个时间才写,其实也是无奈。本来计划过年写的,没想到Steam竟然开了个农历春节特惠,然后就被各种游戏打了,辣鸡平台,敛我钱财,颓我精神,耗我青春,害我单身

以下全都是个人看法,如果有不认同的地方,请大吼一声“傻逼写的啥”然后关闭页面

转职

本命年终于快过去了,不知道是不是因为没有穿红裤衩,这一年有很多不顺心的事情,不过也有很多好事。这一年最重要的事情就是顺利从一只学生狗转职为一只社畜。四月份毕业之后以前端工程师的职位入职天猫,到现在也差不多工作一年了。这里写一下干这行以来对于“前端”这个行业的看法和感悟。

前端工程师在校园里是一个复杂度和地位都被严重低估的职位。老师们对发展迅猛的前端技术不够了解,更是有不少“师兄师姐”在介绍自己找工作经验时“xx天速成就找到一份前端工作”、“公司招聘前端也不问会什么,就问你肯不肯踏实干这行,肯干进去再教你”。当然学校里的项目里也会有页面,但由于老师和学生对前端各种技术的陌生,大多数都是使用jQuery堆积代码完成功能了事。在大家眼里,写页面没啥技术含量。

前端的很多基础知识,都无法在学校学到。学校老师交给学生的大多都是基础知识,比如算法、数据结构、编译原理、操作系统、计算机网络这些,而针对于特定领域就很少涉及了。见过学校开设C、C++、Java、PHP等语言的课程,却从来没有看到过开设JavaScript、CSS、HTML的课程。网页设计的选修课倒是有(我报了,很遗憾整个教室只有20人不到),但这里所谓的网页设计并不是前端那一套技术栈,而是Dreamweaver使用入门。前端技术栈在校园里没有普及。

就这样产生了一个很有意思的现象:大堆公司喊缺前端,而学生们并不知道前端是什么,怎么去学习前端知识(最近两年稍微好点了),他们更多的是去学Java、C++,走着师兄师姐们走过的道路。有幸现在已经有百度这种大公司注意到了这点,到高校里开了不少前端知识讲座,还在GitHub上搞了培训项目。

希望各个大公司的前端部门能够更多的走进校园,给对前端感兴趣的小朋友们一点指导。

React

去年上半年4月份入职打杂了一段时间之后,就开始学习React并且进行了一些实战演练:

  1. 产出一篇文章轻松入门React和Webpack,意外的很受好评,segmentfault上超过了200的收藏,tmall的github博客上也有不少评论。可惜现在文章中已经有很多不适用了,babel大版本到6发生了不少变化,本地调试也改成中间件了

  2. 把自己的博客lingyu.wang改成了全React实现,代码在这里,用react-router做了个单页面应用,底层依然基于hexo,自己给hexo写了个generate插件把所有数据生成json而不直接生成页面,为了练手已经丧心病狂了,SEO什么的完全不在乎

  3. 写了一些脚手架和组件,乱七八糟参见玩具箱

由于负责天猫最复杂的前端应用之一的开发和维护,有不少老代码,也由于迁移成本太大没有办法在Java层上插入Node.js中间层,各种模块之间各种耦合,中间也写了篇用React做重构时的一些思考。当时项目里存在以下几种耦合:

  1. 最蛋疼的就是压根就没有模块化,所有代码都在一个文件里,2k+行的jQuery那种。最老式的写法,也是在学校项目里看到最多的方式,选择器拿到DOM然后操作绑事件,大多多年前的代码或者是后端兼职写的。基本上没有可维护性。解决这种基本上就靠推倒重写了,考虑投入产出比看懂它的成本还不如重新写一个来得快

  2. 稍微好一点的是增加了模块化,但模块内部和之前思路没什么区别,只是把上面那种代码分开多个文件放了。不同的模块在模块内部操作相同的HTML,一旦其中一个模块改变了HTML结构,其他模块直接就bug了...

  3. 再好一点增加了前端模板引擎,各个模块都在内部使用模板引擎渲染自己的HTML,模块初始化传入一个容器,只要容器不冲突,模块之间就不会基于HTML耦合。模块会暴露一些接口,通过模块管理器获取实例相互之间直接相互调用,这样依旧是强依赖,两个模块互相依赖,一旦其中一个换了,接口变了,另外一个模块也需要改变自己的代码。

  4. 模块内部完全黑盒,只要一个容器,里面的内容由模块自己控制。模块有数据的入口和出口,入口就是一些由父模块或页面传入的配置,出口则是一些由父模块或页面传入模块回调函数,回调函数里面附带传出的数据,而HTML相互之间无法互相修改,改了就报错。没错这就是React

最后重构的方案定下来是React+AMD+Gulp(你没有看错,没有打包,没有webpack),之所以不打包主要是有老代码,组件页都没发布到npm上,而且由于阿里的CDN支持combo,所以也就不做打包了,至于使用React做重构,主要是由于以下几个原因:

  1. 使用React实现的模块和组件完全黑盒,Web Component理念,标签使用方便快捷。不会引入新的维护成本

  2. 组件容易抽离,形成沉淀。我曾经把一部分新业务使用React实现,其中的不少组件稍加完善就沉淀出一些基础组件,没有剥离成本

  3. 模块相互之间不会污染,没办法直接修改其他模块的DOM,改了就报错,然后QA就提着刀杀过来了

  4. 商家应用,允许全异步,富交互功能型页面,性能不太烂都能接受

目前新业务已经完全基于React开发,组件库也基本上沉淀出来了,而老业务也在通过一次次需求像React迁移

模块的实现和通信

个人比较倾向于完全区分模块和组件这两个概念。组件是完全没有业务逻辑的功能单元,比如下拉框啊、日期选择啊这种,组件只专注自身的功能。组件可能会有嵌套,但当发生了嵌套时,对外就是一个组件,父组件内部的子组件对外将完全不可见,它的行为也将完全由父组件控制。组件是复用的单元,应当更多的形成沉淀。而模块则包含业务逻辑,同时模块还承载了一个比较重要的功能:和其他模块通信。

所以大体上一个应用就是:应用被划分为多个业务模块,这些模块逻辑上是扁平的,他们采用统一的通信机制进行通信,一个模块上的数据发生变更时,会通知一个全局的通信中心,采用pub-sub机制将数据递交给其他模块,其他模块拿到数据后影响自己的渲染。模块内部使用了很多的组件,但模块内部所有渲染使用的数据都由模块直接进行管控,树形传递给各个组件。可以这么想,模块内部类似Redux实例,而页面上有多个Redux实例,它们再通过一个统一的pub-sub中心进行通信。为什么不整个应用直接做一个Redux实例呢?主要是因为要考虑到跨BU合作和新老多种技术兼容。模块内部想怎么玩怎么玩,可以是React实现,可以使Vue实现,可以是原生js实现。模块作为一个通信单元只要符合统一pub-sub通信接口即可。

这套理念也确实实现并且落地到了不少页面中,但这样玩显然还不够过瘾。React终究是前端在玩,前端写React组件、模块和页面很爽,但是数量大了一样要加班一样爽不起来。这里就在考虑有没有可能降低门槛,把事情交给别人来做,比如后端。首先组件肯定是前端来写了,没多少后端能写好前端组件的,如果能写好,他就不是后端而是全栈了,这种人就应该果断拉过来干前端堵缺口。那么有没有可能把模块和页面交给他们来写,而前端只提供一些组件呢?

让后端写,最重要的一点就是给模板赋能,毕竟后端只能接触到模板。这么一想,这不特么就是React和MVVM相结合么,把React的Virtual DOM或者说JSX标签和MVVM的DOM模板一样写到HTML上,多么Web Component化啊。这里说一下为什么不直接用MVVM比如Vue,而是用React。MVVM确实对后端开发工程师很友好,但这些组件是我们平常开发前端应用(前端部分前端自己负责的复杂业务)时沉淀下来的,这么一大批组件让我们再去重写一份MVVM的太蛋疼了,后期维护两份也比较吃力。就这样开始尝试,经过一段时间调研后,发现react-templates,可以把模板字符串转化为React.createElement,当时用browserify给它打了一个包,尼玛压缩后好像有500K+(未gzip),它是针对Node.js的。于是乎把它整个代码大体上进行了重写,移除了lodash,自己重写了使用的一些工具方法,然后又重写了模板解析部分,采用浏览器的XML解析器,又移除了esprima等语法校验,然后又加了一堆定制化,最后压缩后20K左右(未gzip),终于可以在页面上用了...最后大体上就实现了一套这样的方案:

  • 一堆日常沉淀的React组件

  • 一个全局的pub-sub通信工具负责模块的通信

  • 一个React实现的Module负责充当模块的角色

Module内部使用React Templates字符串模板编译成Virtual DOM的函数来进行渲染,可以通过一些“控制指令”来控制渲染结果,组件上面可以通过一些“通信指令”来递交组件的数据给Module这个模块。而Module模块数据发生变更后会触发整个模块重绘,数据又会重新传递下来给组件并变更组件的渲染结果。

Module外部则是通过全局的pub-sub通信工具来负责模块通信,Module负责与这个通信工具进行直接交互来进行数据通信。通信建立桥接的方式也是在Module上定义一些“通信指令”

最后,整个系统都采用React Templates来实现,整个页面实现和通信全部写在模板上,页面上只有一堆组件(可以在模板里动态require,模板会在编译期提取然后拉取完成了才进行初始化)。没有任何业务js逻辑存在。

这样实现了好几个页面我已经成功上线跑了几个月了,还有几个正在实现中,看上去很美好。但后来还是发现了一些问题:模块通信过程太复杂,一个完整的数据流转过程通过指令很难很直观的展现,一个看起来很简单的交互中间可能会经过4-5步数据流转,甚至包括一些alert、confirm等等,想让后端写不太可能,连其他前端都看不太懂数据如何流转。这一块还有太多可以优化的地方。

流程自动化

去年下半年我参与了部门统一使用的前端流程自动化工具的维护及改造,同时负责了商家端通用组件的脚手架的开发和维护。花了不少时间在前端流程自动化上。前端经过这么多年的发展,页面上承载的交互、功能、逻辑不断增加,项目逐渐变得庞大,维护和开发成本也随之增加,但依然招不到人。__这里顺带打个广告,如果想来天猫前端的请发邮件至lingyucoder@gmail.com__。流程自动化也就愈发重要。最理想的状况就是,只要是机械能够完成的工作,人就不要参与了。用一些脚本代替简单重复的劳动,这样我们也就可以少加点班了。

Node.js

Node.js给前端开发流程自动化带来了福音。从目前部门前端开发流程来看,主要就是以下几个步骤,括号里是对应的开源工具:

  1. 创建项目(Yeoman)

  2. 构建以及监听文件变化自动构建(Gulp、Webpack、Grunt,Grunt估计现在用的很少了)

  3. 本地调试,资源代理(Koa、Express+各种定制的中间件)

  4. 自动化测试、代码覆盖率测试(mocha、istanbul、should/chai/expect、phntomjs、karma)

  5. 文档自动化生成(自己基于AST提取,或者直接用jsdoc之类的工具)

  6. 自动化校验、发布(一些脚本)

这里主要聊聊构建、本地调试和自动化测试

构建

现在的构建过程已经不再像以前那样压缩一下就简单,构建过程中往往会拿代码生成语法树然后做各种操作:

  1. Babel爽爽写ES6甚至ES7

  2. 将代码中的注释转化为监控打点(istanbul我记得就是基于语法树给每个语法分支包一层打点层,汇总数据)

  3. 将代码中的注释提取生成文档(React组件自动生成props文档就是这种方式)

  4. 将commonjs规范的代码转化为各种各样的模块化解决方案(AMD、KMD、UMD等等)

  5. 提取模块间的依赖关系,梳理应用模块依赖关系,绘制模块依赖树

  6. Webpack打包

  7. 各种语法检查(eslint,jshint),有时候还会有一些定制化的校验

  8. ...

现在大家都比较中意webpack,依赖打包使得资源请求数大幅度减少(使用不支持combo的CDN服务的公司肯定很开心)。我个人还是对于webpack有一些顾虑,主要有两点

  1. 不太赞同浏览器内使用的资源也发布到npm上。浏览器上和Node.js通用的代码可能也就不到5%(lodash啊,underscore啊,moment啊这种),在npm上找到一个模块还要确认到底哪个场景下可用。使用的即便是这些可以共用的库,也会有一个很蛋疼的问题:Node.js的模块往往大而全实现尽可能多的功能,而页面使用的模块则是尽可能小而美,资源加载量尽可能少。比如只用到时间格式化和反格式化就引入一整个moment(moment真特么大,我一般就格式化反格式化,喜欢用fecha),对于Node.js也许没什么压力,但对于页面就很蛋疼了。现在rollup试图解决这个问题,还是比较值得看好的

  2. 不同版本重复打包的问题。这个问题比较头疼。如果一个项目依赖了两个组件,而这两个组件引用了一个库的两个不同版本,这个库就会被打包两份,于是乎代码量就duang一下增大了。目前依旧没有看到比较好的方式来解决。虽然可以用peerDependencies对一些基础库(比如React这种)做一下处理,也只是缓解一些罢了。

另外吐槽一句,webpack配置真繁琐啊,个人目前倾向的方案是使用webpack+gulp,webpack负责打包和构建,其他的工作依旧交给gulp,我喜欢stream(不是steam)

本地调试

前端资源本地调试其实挺简单的,就是把线上使用的资源代理到本地资源。由于现在一般都会使用CDN来承载这些资源(如果你们公司不用CDN,请找你们老板拨点经费买个CDN服务吧),大致上也就是几个步骤:

  1. 把CDN的域名通过host指向本地

  2. 在本地80(HTTP)或443(HTTPS)端口开启对应的代理服务,根据请求查找本地资源返回

  3. 如果涉及需要开启多个服务,将各个服务开在不同的端口后在80或443端口加个nginx层做转发

目前部门里面用的是Koa+中间件实现了这里面所有的内容。Koa现在也2.0了,使用新版本的Promise的co,用起来还是很爽的

如果一旦涉及到本地模板的调试,就很蛋疼了,基本上是模拟数据,这里懒得扯了。

自动化测试

对于自动化测试这一块,在学生时代一直觉得测试麻烦,没啥收益。但实际上自动化测试对于开发效率提升很大。之前参与部门统一构建工具的改造,有任何修改就跑一遍测试用例和代码覆盖率,效率非常高,还非常容易形成沉淀,一旦有bug,就把bug会发生的场景也做成一个测试用例,方便后人接手。而且把代码接入像travis这样的持续集成平台后,代码质量更有保证了,任何一次push都会自动触发测试,即便是pull request里的代码也可以保证质量。现在写代码覆盖率低于90%就觉得各种不爽,一定要提到90%以上。

对于Node.js的模块,测试很方便,除了命令行工具可能需要加像sinon这样的模块来监听stdio以外,其他的基本上都能直接在代码中模拟环境。由于个人写Node.js代码喜欢拆分的很细,每个逻辑单元都用co、curry做包装,所以特别喜欢使用mocha+should,should 8.0+直接支持Promise,爽歪歪,

对于浏览器里跑的代码,测试和覆盖率就比较麻烦了,首先模拟环境比较蛋疼,大致上3种方案:

  1. 构建一个测试页面,人肉直接到虚拟机上开各种浏览器跑测试页面(比如f2etest),这个问题就是不好持续化集成,人肉工作较多

  2. 使用phantomjs构建一个伪造的浏览器跑单元测试,大致上就是先用gulp-istanbul给代码打点,然后拿mocha-phantomjs跑包含测试用例的页面,最后通过hook拿到结果用istanbul生成可视化的覆盖率页面,蛋疼就是phantomjs毕竟是Qt的webkit,不是真实环境,phantomjs也是各种坑

  3. 通过karma调用本机各种浏览器进行测试,这个现在还没玩的很6,还在研究中。还是有不少问题没解决,毕竟用的mac,去哪儿找IE 8,囧,更别说移动端那么多机型

对于测试个人一直坚持一个观点:基于投入产出比来做测试。由于维护测试用例也是一大笔开销(毕竟没有多少测试会专门帮前端写业务测试用例,而前端使用的流程自动化工具更是没有测试参与了)对于像基础组件啊,基础模型啊之类的不常变更且复用较多的部分,可以考虑去写测试用例来保证质量,但对于迭代较快的业务逻辑以及生存时间不长的活动页面之类的就别花时间写测试用例了,维护测试用例的时间大了去了,不如喝杯茶冷静下让QA他们去测吧。

学习

这一年由于转职社畜各种忙的要死,导致没有多少时间静下心来看书了,文章也写得少了。于是更倾向于每天水一水我的github(中间一大段空白因为3DS到货了,鏖战怪物猎人4G,这游戏真特么好玩),写一些脚手架啊、组件啊、小工具啊啥的。之前觉得一些开源的logger不好用,就自己写了个linglog,之前负责一个业务变更比较多总是打tag发布,就写了个自动发布程序publishy,它会在发布前做一些校验防止我没有commit或者没有add之类的。还有像React组件自动化提取props做文档弄了个react-prop-table,以及对应的配套的markdown内容段自动更新gulp插件gulp-insert-md。对应还有一些less依赖关系解析啥的弄了less-tree,然后有个需求要在模块更新时自动输出最新更新有哪些变动于是有了changelogy,然后还有几个自己弄得带单元测试、代码覆盖率测试、travis持续集成、eslint等的脚手架:React组件脚手架generator-lingyu-react-component, Node模块脚手架generator-lingyu-node-modules, gulp插件脚手架generator-lingyu-gulp-plugin, 命令行工具脚手架generator-lingyu-cli-modules。写这些玩意过程中学到了不少Node.js的姿势,虽然离一个真正的Node.js工程师差的太远

另外一点是入职后全面从sublime切到atom了,python苦手还是伤不起,咱用atom有插件需求找不到自己写一个,比如之前给xtemplate模板写了个atom语法高亮和snippet插件atom-language-xtpl,感觉比之前用sublime爽多了

还花了点钱买了SnippetsLab这个软件,用来放一些代码片段超好用,我在里面放了不少自己平常写的一些小的工具函数,比如clone啊,unique啊,param和unparam啊这种,需要的时候就搜一下复制出来,方便快捷

生活

今年玩的游戏不多,因为3DS到货了,先说几个3DS游戏:

  1. 怪物猎人,沉迷了一段时间,甚至一整天一整天的和各种龙做斗争。现在怪物猎人累计游戏时间应该有250小时了...

  2. 口袋妖怪X:硬着头皮啃英文,撸宠系统好棒,每天撸一撸自己的宠,看到他们开心的样子自己也开心。通了一周目就懒得啃英文了

  3. 牧场物语,在同事推荐下玩了,模拟经营农场,种菜、种果树、养各种动物卖钱...玩了几十个小时吧,玩不下去了...后面每天刷牛挤牛奶,撸鸡一天就结束了...没领悟到游戏的乐趣

  4. 路易吉鬼屋,超级玛丽里的弟弟被拉到鬼屋里探险的故事,乐趣就是看路易吉这个大写的怂货被各种鬼吓尿...解密游戏,其实挺好玩的

  5. 马里奥赛车:和跑跑道具赛差不多,玩道具赛,道具比较少,赛道也少,难度也低。

  6. 勇气默示录:回合制RPG,还在龟速通关中,画面很棒,3D的人物很Q,相当不错的游戏

然后说一些PC的:

  1. 侠客风云传:情怀!绝对的情怀!作为一个当年武林群侠传的狂热爱好者,侠客风云传我通关了至少6次,乞丐、东厂、盟主、霸图、天王各种都通了一遍。湘云还是一如既往的女神,希望徐大早日重制金庸群侠传

  2. 仙6:这个战斗系统特么什么鬼...没有玩下去的动力啊

  3. 堡垒(bastion):很精致的游戏,画面唯美,歌曲好听,剧情不长但很好玩。

  4. 进化之地2(evoland2):尼玛一个游戏能这么玩我算是长见识了,集DQ、塞尔达传说、超级玛丽、拳皇、双截龙、炸弹人、游戏王、洛克人、1943等等于一身的游戏也没谁了

  5. 阿玛拉王国:和老滚有点像,不过比老滚有打击感,重要的是有WOW那种装备系统。老滚的装备系统是个鬼...可惜没有老滚那么多mod,毕竟少女卷轴

  6. 饥荒:这游戏太特么难了,一到冬天就死...得找个大神带我

春节剁手买了昆特牌3和龙腾世纪,有时间玩一玩。最后重申一句:辣鸡平台,敛我钱财,颓我精神,耗我青春,害我单身

感情

看他们的总结都有这个,于是我加上了

光棍年数++

总结

前端发展太快,要学的太多。本来去年计划学React Native的,结果只写了个Demo...Electron也是只跑了个HelloWorld...Node.js的服务器也只是搭了个简单的...canvas也是只写了一些小Demo。希望新的一年技术不掉队,能够多学点这些之前想学没学的东西。另外希望能够沉淀出自己的组件库,以后快速搭建一个页面也方便一些


天镶
5.3k 声望566 粉丝

尘世间一个迷途的小码农